package com.mdhw.common.annotation;


import com.mdhw.common.enums.MessageEnum;
import com.mdhw.common.exception.BusException;
import com.mdhw.utils.IpAddressUtil;
import com.mdhw.utils.JwtUtil;
import com.mdhw.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.UUID;

@Slf4j
@Aspect
@Component
public class AvoidRepeatableCommitAop {
    @Autowired
    private RedisUtil redisUtil;
    private static final String FORM_LOCK = "form_lock";

    @Autowired
    private HttpServletRequest request;

    /**
     * 1. 获取ip地址，访问类，方法名作为Redis key
     * 2. 设置一定时间超时
     * 3. 每次访问生成key，根据key查询是否已存在Redis
     *
     * @param point
     * @author amoyiki
     */
    @Around("@annotation(AvoidRepeatableCommit)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String ip =  IpAddressUtil.getRequestIp();
        // 获取注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        // 目标类，方法
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        String methodKey = String.format("%s#%s", className, methodName);
        int methodHashCode = Math.abs(methodKey.hashCode());
        // 参数 hashCode
        Object[] args = point.getArgs();
        StringBuilder sb = new StringBuilder();
        for (Object arg : args) {
            if (arg != null) {
                sb.append(arg.toString());
            }
        }
        String s1 = sb.toString();
        int paramHashCode = Math.abs(s1.hashCode());

        String loginName = "default";
        String authorization = request.getHeader("Authorization");
        if (authorization == null || !authorization.startsWith("Bearer ")) {
            // 没有token
        } else {
            String token = JwtUtil.getRequestToken(request);
            loginName =  JwtUtil.parseToken(token);
        }

        String key = String.format("%s:%s_%s_%d_%d", FORM_LOCK, ip, loginName, methodHashCode, paramHashCode);

        AvoidRepeatableCommit repeatableCommit = method.getAnnotation(AvoidRepeatableCommit.class);
        long timeout = repeatableCommit.timeout();
        if (timeout <= 0) {
            timeout = 3;  // 默认3秒
        }

        boolean isSet = redisUtil.setIfAbsent(key, UUID.randomUUID().toString(), timeout);
        if (!isSet) {
            log.info("├ [表单重复提交] methodKey={},methodHashCode={}, paramHashCode={}, key={},", methodKey, methodHashCode,
                    paramHashCode, key);
            throw new BusException(MessageEnum.DUPLICATE_POST_ERROR);
        }

        Object o = point.proceed();
        return o;
    }
}
